/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package replica.communication;

import generalResourceLalla.wsFormattedOperation.GetListRequest;
import generalResourceLalla.wsFormattedOperation.TaskEnvelope;
import generalResourceLalla.wsFormattedOperation.DequeueInformation;
import generalResourceLalla.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import replica.transaction.FrontEnd;
import replica.transaction.Operation;

/**
 * It receives operation from FrontEndHandler
 * @author Elena
 */
public class OperationReceiver implements Runnable
{
    List<FrontEnd> frontEnds;
    //private long lastSerial=0; //PER DEBUG
    private int portNum=Costanti.fromFrontEndHandlerToOperationReceiverSocketPort;
    
    Map<Long,Socket> pendingGetListReq=new HashMap<Long,Socket>();
    
    public OperationReceiver(List<FrontEnd> list)
    {
        this.frontEnds=list;
    }
        

    @Override
    public void run() 
    {
        
        ObjectInputStream reader = null;
        ServerSocket receiveSock = null;
        //Socket cs = null;
        boolean youMustCloseSock=true;
    
         try {
             
             System.out.println("OperationReceiver: listening on port : "+portNum);
            receiveSock = new ServerSocket(portNum);
            System.out.println("OerationReceiver: receiveSock = "+receiveSock.getLocalPort());
            while (true) 
            {
                System.out.println("OperationReceiver: waiting for operations");
            
                Socket cs = receiveSock.accept();
                reader = new ObjectInputStream(cs.getInputStream());
                
                //writer = new ObjectOutputStream(cs.getOutputStream());

                    
                //Accept the command (enqueue command or dequeue command)
                Object ob=reader.readObject();
                System.out.println("cs is closed -1: "+cs.isClosed());
               // reader.close();
                System.out.println("cs is closed 0: "+cs.isClosed());
                
              if   (ob != null) 
              {
                 if(ob instanceof DequeueInformation)
                       {
                           DequeueInformation di=(DequeueInformation)ob;
                           System.out.println(FrontEnd.communicationPrefix+"OperationReceiver:Reading DequeueInformation: "+di.toString());
                           Operation op=new Operation(di.getSerial(),Operation.OperationType.DEQUEUE,null,di.getNodeId());
                           FrontEnd frontEnd= getFrontEnd(frontEnds,di.getNodeId());
/*                           //PER DEBUG
                           try{
                               if (op.getSerialNumber()==lastSerial+1) lastSerial=op.getSerialNumber();
                               //else throw new Exception("lastSerial="+lastSerial+"; op="+op);
                            }
                           catch(Exception e){e.printStackTrace(); System.exit(-21432);}  
*/                           //FINE PER DEBUG
                           frontEnd.doOperation(op);
                           //System.out.println("nodo: "+di.getId()+" lista:"+frontEnds.get(i).toString());
                       }
                 
                 //gestisce richiesta di enqueue
                       else if(ob instanceof TaskEnvelope){
                            TaskEnvelope a =(TaskEnvelope)ob;
                            System.out.println(FrontEnd.communicationPrefix+"OperationReceiver:Reading taskEnvelope: "+a.toString());
                            Operation op=new Operation(a.getSerial(),Operation.OperationType.ENQUEUE,a.getTask(),a.getNodeId());
                            FrontEnd frontEnd= getFrontEnd(frontEnds,a.getNodeId());
/*                            //PER DEBUG
                           try{
                               if (op.getSerialNumber()==lastSerial+1) lastSerial=op.getSerialNumber();
                               
                            }
                           catch(Exception e){e.printStackTrace(); System.exit(-21432);}  
*/                           //FINE PER DEBUG
                           
                            frontEnd.doOperation(op);
                            //System.out.println("nodo: "+a.getSender()+" lista:"+frontEnds.get(i).toString());
                       }
                       
                       
                       
                       else if(ob instanceof GetListRequest)
                       {
                           youMustCloseSock=false;
                            System.out.println("cs is closed 1: "+cs.isClosed());
                            GetListRequest req=(GetListRequest)ob;
                            System.out.println("cs is closed 2: "+cs.isClosed());
                            System.out.println(FrontEnd.communicationPrefix+"OperationReceiver: Ricevuta richiesta GETLISTREQUEST");
                            
                            //Metto da parte il socket del FrontEndHandler che mi ha fatto questa
                            //richiesta di getList. In questo modo, potro' restituirgli la lista
                            //appena disponibile
                            this.pendingGetListReq.put(req.getId(),cs);
                            System.out.println("cs is closed 3: "+cs.isClosed());
                           
                            FrontEnd frontEnd= getFrontEnd(frontEnds,req.getId()); //ottengo il front end corrispondente
                            Operation op=Operation.buildGetListOperation(req.getId()); 
                            System.out.println("cs is closed 5: "+cs.isClosed());
                            frontEnd.doOperation(op);
                            System.out.println("Sono in OperationReceiver "+op);
                            System.out.println("cs is closed 6: "+cs.isClosed());   
                            
                    
                                //fine della finta
                            /*ObjectOutputStream writer = new ObjectOutputStream(cs.getOutputStream());    
                            writer.writeUTF(prova);
                            writer.flush();
                            writer.close();*/
                           
                       }    
              }else //ob==null
                    throw new NullPointerException("Received ob="+ob);
              System.out.println("cs is closed 7: "+cs.isClosed());
              //writer.close();
              //cs.close();
              System.out.println("cs is closed 8: "+cs.isClosed());
              //PER DEBIG
              if(cs.isClosed() && !youMustCloseSock)
                  throw new Exception("Non dovevi chiuderti, b....");
              // FINE PER DEBUG
              
              if (cs!=null && youMustCloseSock)
                  cs.close();
              
              
            }
        } catch (Exception e) {
            System.out.println("Exception occurred: " + e.toString());
            e.printStackTrace();
        }finally {
             try{/*
                if(cs!=null)
                    cs.close();*/
                if(reader!=null)
                    reader.close();
                //if(writer!=null)
                //  writer.close();
                if (receiveSock!=null)
                    receiveSock.close();
             }catch(IOException ioe)
             {ioe.printStackTrace();System.exit(-7561);}
        }
    }
    
    
    public  void sendListToNode(TaskList list, long nodeId)
    {
        Socket returnSocket=null;
        try{
            returnSocket = this.pendingGetListReq.remove(nodeId);
                           JAXBContext context = JAXBContext.newInstance(TaskList.class);
                           ObjectOutputStream writer = new ObjectOutputStream(returnSocket.getOutputStream());
                           Marshaller m = context.createMarshaller();
                           m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                           m.marshal(list, writer);
                           writer.flush();
                               
                            writer.close();
        }  catch(Exception ex){
                    System.out.println(ex);
                }
        finally {
             try{
                if(returnSocket!=null)
                    returnSocket.close();
             }catch(IOException ioe)
             {ioe.printStackTrace();System.exit(-7561);}
        }
        
    }
    
    public static FrontEnd getFrontEnd(List<FrontEnd> list, long id) 
            throws NullPointerException
    {
        for (FrontEnd fe:list) 
            if (id == fe.getNodeId())
                return fe;
        throw new NullPointerException("No front end "+id+" is present in "+list);
    }
}
